#ifndef __SOCKET_HPP__
#define __SOCKET_HPP__
#include"Logger.hpp"
#include"InetAddr.hpp"

#include<iostream>
#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>

static const int gbacklog = 16;
static const int gsockfd = -1;

enum{
    OK,
    SOCKET_CREATE_ERR,
    SOCKET_BIND_ERR,
    SOCKET_LISTEN_ERR
};

//使用多态的方式实现对套接字的封装
class Socket
{
public:
    virtual ~Socket(){};
    //创建套接字、绑定、监听
    virtual void CreateSocketOrDie() = 0;
    virtual void BindSocketOrDie(int port) = 0;
    virtual void ListenSocketOrDie() = 0;

    //服务器端->获取连接
    // virtual std::shared_ptr<Socket> Accept(InetAddr* peer) = 0;
    virtual int Accept(InetAddr* peer) = 0;
    //客户端：发起连接请求
    virtual bool Connect(InetAddr&server) = 0;

    //获取文件描述符
    virtual int Sockfd() = 0;
    //关闭文件描述符
    virtual void Close() = 0; 

    //获取数据recv，写数据send;将其返回值交给上层处理判断
    virtual ssize_t Recv(std::string* out) = 0;
    virtual ssize_t Send(const std::string& in) = 0;

public:
    //方法集
    //服务器端创建监听套接字
    void BuildListenSocket(int _port)
    {
        CreateSocketOrDie();
        BindSocketOrDie(_port);
        ListenSocketOrDie();
    }
    //客户端创建套接字
    void BuildSockfd()
    {
        CreateSocketOrDie();
    }
    
};

class TcpSocket : public Socket
{
public:
    TcpSocket():_sockfd(gsockfd)
    {}
    //带参的构造
    TcpSocket(int sockfd):_sockfd(sockfd)
    {}
    ~TcpSocket(){}
    void CreateSocketOrDie()
    {
        //socket
        _sockfd = socket(AF_INET , SOCK_STREAM , 0);//面向字节流
        if(_sockfd < 0)
        {
            LOG(LogLevel::FATAL) << "创建 socket 错误";
            exit(SOCKET_CREATE_ERR);
        }
        LOG(LogLevel::INFO) << "创建 socket 成功";
    }
    void BindSocketOrDie(int port)
    {
        InetAddr server(port);
        int n = bind(_sockfd , server.Addr() , server.Length());
        if( n != 0)
        {
            LOG(LogLevel::FATAL) << "绑定 socket 失败";
            exit(SOCKET_BIND_ERR);
        }
        LOG(LogLevel::INFO) << "绑定 socket 成功";
    }
    void ListenSocketOrDie()
    {
        int n = listen(_sockfd , gbacklog);
        if(n < 0)
        {
            LOG(LogLevel::FATAL) << "监听 sockfd 失败";
            exit(SOCKET_LISTEN_ERR);
        }
        LOG(LogLevel::INFO) << "监听 sockfd 成功";
    }

    //服务器端->获取连接
    //普通文件描述符也封装一下
    // std::shared_ptr<Socket> Accept(InetAddr* peer)
    int Accept(InetAddr* peer)
    {   
        //accept 在获取连接的同时，需要得到发送端的地址信息;成功之后返回新的文件描述符
        struct sockaddr_in client;
        socklen_t len = sizeof(client);

        int sockfd = accept(_sockfd , (struct sockaddr*)&client , &len );
        // std::cout << "sockfd: " << sockfd << std::endl;

        if(sockfd < 0)
        {
            //服务器端获取连接失败并不能是其进程结束的原因；服务器端获取连接失败，重新继续获取连接就可以了
            LOG(LogLevel::WARNING) << "获取连接失败";
            return -1;
        }
        LOG(LogLevel::INFO) << "获取连接成功";
        
        //处理需要返回的数据
        peer->Init(client);
        return sockfd;
    }
    //客户端：发起连接请求
    bool Connect(InetAddr&server)
    {
        //connect;成功返回0，失败返回-1
        // std::cout <<server.Ip() << ":" << server.Port() << "//" <<server.ToString() << std::endl;
        int n = connect(_sockfd ,server.Addr() , server.Length());
        if(n==0) return true;
        return false;
    }

    int Sockfd()
    {
        return _sockfd;
    }
    void Close()
    {
        close(_sockfd);
    }
    
    ssize_t Recv(std::string* out)//输出型参数
    {
        char outbuffer[1024];
        outbuffer[0]=0;
        ssize_t n = recv(_sockfd , outbuffer , sizeof(outbuffer)-1 , 0);//确保 \0 有位置可以存放
        if( n > 0)//读取成功
        {
            //处理输出型参数
            outbuffer[n] = 0;
            *out = outbuffer;
        }
        return n;
    }
    ssize_t Send(const std::string& in)//输入型参数
    {
        ssize_t n = send(_sockfd , in.c_str() , in.size() ,0);
        return n;
    }

private:
    int _sockfd;
};
#endif